/*******************************************************************************
 * Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 * Copyright (c) 2011- kotemaru@kotemaru.org
 ******************************************************************************/
package org.kotemaru.gae.dstool.server;
import com.google.appengine.api.datastore.*;
import com.google.appengine.api.users.User;
import com.google.appengine.api.memcache.*;
import java.text.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;

import org.kotemaru.gae.dstool.DsToolConst;
import org.kotemaru.util.json.*;
import org.kotemaru.util.*;

/**
 */
public class EntityParser implements DsToolConst{

	public static Key parseKey(String keyStr) {
		JSONParser parser = new JSONParser();
		Map map = parser.parse(keyStr);
		return toKey(map);
	}

	public static Key toKey(Map map) {
		if (map == null) return null;

		String kind = (String) map.get("kind");
		String name = (String) map.get("name");
		Long id = (Long) map.get("id");
		Key parent = toKey((Map) map.get("parent"));

		if (name != null) {
			return KeyFactory.createKey(parent, kind, name);
		} else {
			return KeyFactory.createKey(parent, kind, id);
		}
	}


	private DatastoreService datasotre = DatastoreServiceFactory.getDatastoreService();
	private int storedCount = 0;

	public EntityParser() {
	}

	public int getStoredCount() {
		return storedCount;
	}

	public void parseAndStore(String kind, Reader source) {
		JSONParser parser = new JSONParser(source);

		int tkn = parser.nextToken();
		while (tkn == '{') {
			Map map = parser.pObject(tkn, null);
			store(kind, map);
			storedCount++;
			tkn = parser.nextToken();
			if (tkn != '{') tkn = parser.nextToken();
		}
	}

	public void store(String kind, Map map) {
		Entity entity;
		Key key = makeKey(kind, map.get(__KEY__));
		if (key != null) {
			entity = new Entity(key);
		} else {
			entity = new Entity(kind);
		}

		Iterator<Map.Entry<String, Object>> ite = map.entrySet().iterator();
		while(ite.hasNext()){
			Map.Entry<String, Object> ent = ite.next();
			String name = ent.getKey();
			if (!__KEY__.equals(name)) {
				entity.setProperty(name, fromJson(ent.getValue()) ); 
			}
		}
		datasotre.put(entity);
	}

	private Key makeKey(String kind, Object __key__) {
		if (__key__ == null) return null;
		if (__key__ instanceof Map) return toKey((Map)__key__);
		if (__key__ instanceof Long) {
			return KeyFactory.createKey(kind, (Long)__key__);
		}
		if (__key__ instanceof String) {
			return KeyFactory.createKey(kind, (String)__key__);
		}
		throw new RuntimeException("Bad __key__ parameter type "+__key__.getClass());
	}

	private Object fromJson(Object val) {
		if (val instanceof List) return fromJson((List) val);
		if (val instanceof Map) return fromJson((Map) val);
		return val; // String,Long,Double,Boolean
	}
	private Object fromJson(List val) {
		List list = new ArrayList(val.size());
		Iterator ite = val.iterator();
		while(ite.hasNext()){
			list.add(fromJson(ite.next()));
		}
		return list;
	}

	private Object fromJson(Map map) {
		String type = (String) map.get("type");
		Object value = map.get("value");

		if (KEY.equals(type)) return toKey((Map)value);
		if (DATE.equals(type)) return toDate(map);
		if (DOUBLE.equals(type)) return Double.valueOf(value.toString());
		if (SBLOB.equals(type)) return new ShortBlob(HexUtil.decodeHexBytes(value.toString()));
		if (BLOB.equals(type)) return new Blob(HexUtil.decodeHexBytes(value.toString()));
		if (TEXT.equals(type)) return new Text(value.toString());
		if (LINK.equals(type)) return new Link(value.toString());
		if (USER.equals(type)) return toUser((Map)map);
		throw new RuntimeException("Unsupported "+type);
	}

	private Object toDate(Map map) {
		String fmt = (String) map.get("fmt");
		Object value = map.get("value");
		if (fmt == null) return new Date(((Number)value).longValue());
		try {
			SimpleDateFormat format = new SimpleDateFormat(fmt);
			return format.parse(value.toString());
		} catch (java.text.ParseException e) {
			throw new RuntimeException(e);
		}
	}

	private Object toUser(Map map) {
		String email = (String) map.get("email");
		String authDomain = (String) map.get("authDomain");
		return new User(email, authDomain);
	}
}